/*
 * Adam Blackwell
 * ada2358@ccs.neu.edu
 */

/***********************************************************************
 * Ship class
 * 
 * Implements a ship artifact
 *
 * Signature:
 *  public static Ship create (String name, Coord position, int energy);
 *  public void install ();
 *  public void destroy ();
 *  public Coord position ();
 *  public String name ();
 *  public void report (String message);
 *  public void changePosition (Coord target);
 *  public boolean isOrbitable ();
 *  public void takeDamage (int damage);
 *  public abstract char display ();
 *  public abstract String toString ();
 *  public int energy ();
 *  public void move (int dx, int dy) {
 ***********************************************************************/

/**
 * Ship:
 * @author Adam
 */
class Ship extends Artifact {
  private int energy;
  
  /**
   * Private Constructor:
   * @param n
   * @param p
   * @param e
   */
  protected Ship(String n, Coord p, int e) {
    super(n, p);
    this.energy = e;
  }
  
  /**
   * Factory Method to Create:
   * @param n
   * @param p
   * @param e
   * @return
   */
  public static Ship create(String n, Coord p, int e) {
    Ship sh = new Ship(n, p, e);
    sh.install();
    return sh;
  }
  
  /* (non-Javadoc)
   * @see Artifact#display()
   */
  public char display() {
    return '^';
  }
  
  /* (non-Javadoc)
   * @see Artifact#toString()
   */
  public String toString() {
    return "Ship " + this.name() + " " + this.position().toString();
  }
  
  /**
   * Energy:
   * @return
   */
  public int energy() {
    return this.energy;
  }
  
  /* (non-Javadoc)
   * @see Artifact#takeDamage(int)
   */
  public void takeDamage(int d) {
    if (d < 0)
      throw new IllegalArgumentException("Ship.takeDamage()");
    report("taking damage " + d);
    useEnergy(d);
    if (energy == 0) {
      report("AAAAAAAAARRRGHHHH! *BOOM*");
      destroy();
    }
  }
  
  /**
   * UseEnergy:
   * @param used
   */
  protected void useEnergy(int used) {
    energy = energy - used;
    if (energy <= 0)
      energy = 0;
    report("energy down to " + energy);
    if (energy == 0)
      report("life-support systems failing");
  }
  
  /**
   * Move:
   * @param dx
   * @param dy
   */
  public void move(int dx, int dy) {
    Coord c = this.position().move(dx, dy);
    int cost = this.position().distanceInt(c);
    if (energy <= cost) {
      report("insufficient power to move as requested");
      return;
    }
    report("attempting to move to " + c);
    if (!(c.within(Coord.create(0, 0), SpaceGame.map().size().move(-1, -1)))) {
      report("target position out of bounds");
      return;
    }
    useEnergy(cost);
    Option<Artifact> target = SpaceGame.map().findArtifactByPosition(c);
    if (target.isNone()) {
      report("moved to " + c);
      changePosition(c);
      return;
    }
    // there's something at the target position
    report("emergency collision avoidance!");
    do {
      Coord shift = Util.randomShift(5);
      c = c.add(shift);
      useEnergy(5);
    } while (!c.within(Coord.create(0, 0), SpaceGame.map().size().move(-1, -1))
             || !SpaceGame.map().findArtifactByPosition(c).isNone());
    report("moved to " + c);
    changePosition(c);
  }
  
  /**
   * Fires on the position that is dx, dy spaces away.
   * @author Adam
   * @param x
   * @param y
   */
  public void fire(int x, int y) {
    // Is there enough Energy:
    if (energy < 6) {
      report("insufficient Energy to fire");
      return;
    }
    // Firing, uses five units of energy:
    // Target and (Potentially) Victim:
    Coord target = this.position().move(x, y);
    Option<Artifact> victim = SpaceGame.map().findArtifactByPosition(target);
    // Use Energy:
    useEnergy(5);
    // Prints:
    report("firing to " + target.toString());
    // If we hit nothing:
    if (victim.isNone()) {
      report(this.name() + " reports -- miss");
    // Else if we hit a Planet or Star, do nothing:
    } else if (victim.valOf().toString().startsWith("Star")
        || victim.valOf().toString().startsWith("Planet")) {
      report("direct hit on" + victim.valOf().name());
    // Else if we hit a Base or Ship, do damage:
    } else if (victim.valOf().toString().startsWith("Base") 
        || victim.valOf().toString().startsWith("Ship")) {
      report("direct hit on" + victim.valOf().name());
      victim.valOf().takeDamage(5);
    }
    // Return:
    return;
  }
  
  /**
   * Orbits a nearby Planet or Base if one is there.
   * @author Adam
   * @param to_orbit
   */
  public void orbit(Artifact to_orbit) {
    if (to_orbit.isOrbitable() 
        && to_orbit.position().distance(this.position()) < 2) {
      // Orbit:
      report("orbiting " + to_orbit.name());
      // Recharge:
      this.energy = 100;
      report("energy reserves recharged to 100");
    } else {
      // Nada:
      report("can only orbit a near base or planet");
    }
  }
}
